/**
 * This file was created by Nick Popescu Taylor Garren for use in
 * SpaceTrader
 */

import java.awt.Dimension;
import java.awt.Point;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.JOptionPane;

/**
 * This class generates a Space grid of all the planets in the game.
 * 
 * @author Nick Popescu, Taylor Garren
 * @version 1.0
 */

public class Space implements Serializable {

    /**
     *  These are all of the names for the planets.
     */
    private static final String[] PLANET_NAMES = new String[] {
	    "Acamar",
	    "Adahn", // The alternate personality for The Nameless One in
		     // "Planescape: Torment"
	    "Aldea", "Andevian", "Antedi", "Balosnee", "Baratas",
	    "Brax", // One of the heroes in Master of Magic
	    "Bretel", // This is a Dutch device for keeping your pants up.
	    "Calondia", "Campor",
	    "Capelle", // The city I lived in while programming this game
	    "Carzon",
	    "Castor", // A Greek demi-god
	    "Cestus", "Cheron",
	    "Courteney", // After Courteney Cox
	    "Daled", "Damast", "Davlos", "Deneb", "Deneva", "Devidia",
	    "Draylon", "Drema", "Endor", "Esmee", // One of the witches in
						  // Pratchett's Discworld
	    "Exo", "Ferris", // Iron
	    "Festen", // A great Scandinavian movie
	    "Fourmi", // An ant, in French
	    "Frolix", // A solar system in one of Philip K. Dick's novels
	    "Gemulon", "Guinifer", // One way of writing the name of king
				   // Arthur's wife
	    "Hades", // The underworld
	    "Hamlet", // From Shakespeare
	    "Helena", // Of Troy
	    "Hulst", // A Dutch plant
	    "Iodine", // An element
	    "Iralius", "Janus", // A seldom encountered Dutch boy's name
	    "Japori", "Jarada", "Jason", // A Greek hero
	    "Kaylon", "Khefka", "Kira", // My dog's name
	    "Klaatu", // From a classic SF movie
	    "Klaestron", "Korma", // An Indian sauce
	    "Kravat", // Interesting spelling of the French word for "tie"
	    "Krios", "Laertes", // A king in a Greek tragedy
	    "Largo", "Lave", // The starting system in Elite
	    "Ligon", "Lowry", // The name of the "hero" in Terry Gilliam's
			      // "Brazil"
	    "Magrat", // The second of the witches in Pratchett's Discworld
	    "Malcoria", "Melina", "Mentar", // The Psilon home system in Master
					    // of Orion
	    "Merik", "Mintaka", "Montor", // A city in Ultima III and Ultima VII
					  // part 2
	    "Mordan", "Myrthe", // The name of my daughter
	    "Nelvana", "Nix", // An interesting spelling of a word meaning
			      // "nothing" in Dutch
	    "Nyle", // An interesting spelling of the great river
	    "Odet", "Og", // The last of the witches in Pratchett's Discworld
	    "Omega", // The end of it all
	    "Omphalos", // Greek for navel
	    "Orias", "Othello", // From Shakespeare
	    "Parade", // This word means the same in Dutch and in English
	    "Penthara", "Picard", // The enigmatic captain from ST:TNG
	    "Pollux", // Brother of Castor
	    "Quator", "Rakhar", "Ran", // A film by Akira Kurosawa
	    "Regulas", "Relva", "Rhymus", "Rochani", "Rubicum", // The river
								// Ceasar
								// crossed to
								// get into Rome
	    "Rutia", "Sarpeidon", "Sefalla", "Seltrice", "Sigma", "Sol", // That's
									 // our
									 // own
									 // solar
									 // system
	    "Somari", "Stakoron", "Styris", "Talani", "Tamus", "Tantalos", // A
									   // king
									   // from
									   // a
									   // Greek
									   // tragedy
	    "Tanuga", "Tarchannen", "Terosa", "Thera", // A seldom encountered
						       // Dutch girl's name
	    "Titan", // The largest moon of Jupiter
	    "Torin", // A hero from Master of Magic
	    "Triacus", "Turkana", "Tyrus", "Umberlee", // A god from AD&D, which
						       // has a prominent role
						       // in Baldur's Gate
	    "Utopia", // The ultimate goal
	    "Vadera", "Vagra", "Vandor", "Ventax", "Xenon", "Xerxes", // A Greek
								      // hero
	    "Yew", // A city which is in almost all of the Ultima games
	    "Yojimbo", // A film by Akira Kurosawa
	    "Zalkon", "Zuul" // From the first Ghostbusters movie
    };

    /**
     * Planet array
     */
    
    private Planet[] map;
    
    /**
     * Size of the grid
     */
    
    private final Dimension gridSize;
    
    /**
     * Current player location
     */
    //private Point playerLocation;
    /**
     * The user 
     */
    
    private final Player player;
    
    /**
     * Draw size of planet.
     */
    private static final int DRAWSIZE = 5;
    
    /**
     * The fuel distance ratio
     */
          
    private static final int FUEL_DISTANCE_RATIO = 3;

    /**
     * Generates the given number of planets on a grid of a given size.
     * 
     * @param numPlanets
     *            The number of planets that will be in the game.
     * @param gridSize
     *            The dimensions of the grid that will determine XY coords of
     *            the planets.
     * @param player The user
     */
    public Space(int numPlanets, Dimension gridSize, Player player) {

	this.gridSize = gridSize;
	this.player = player;

	final ArrayList<Integer> xIntPile = new ArrayList();// Pile of all available X
						      // locations.
	final ArrayList<Integer> yIntPile = new ArrayList();// Pile of all available Y
						      // locations.

	/*
	 * Next two For loops populate the piles with possible values.
	 */
	for (int i = 0; i < gridSize.getWidth(); i++) {
	    xIntPile.add(i);
	}

	for (int i = 0; i < gridSize.getHeight(); i++) {
	    yIntPile.add(i);
	}

	if (numPlanets > PLANET_NAMES.length) {// Checks to see if we want more
					      // planets than we have names.
	    numPlanets = PLANET_NAMES.length; // make number of planets equal to // $codepro.audit.disable questionableAssignment
					     // number of names.
	}

	map = new Planet[numPlanets];// Array that holds all the planets.

	final Random rand = new Random();

	for (int i = 0; i < numPlanets; i++) {

	    /*
	     * Grabs random X and Y integers from the piles.
	     */
	    int xRand = rand.nextInt(xIntPile.size());
	    int yRand = rand.nextInt(yIntPile.size());

	    /*
	     * Assigns those X and Y coordinates to the Location of the future
	     * planet.
	     */
	    int xLoc = xIntPile.get(xRand);
	    int yLoc = yIntPile.get(yRand);

	    /*
	     * Removes the used X and Y coordinates from teh piles.
	     */
	    xIntPile.remove((Object) xLoc);
	    yIntPile.remove((Object) yLoc);

	    /*
	     * Makes a new Point with the picked XY coordinates.
	     */
	    Point locationPoint = new Point(xLoc, yLoc);

	    /*
	     * Makes the new planet, with a name and the picked XY coordinates
	     * (in a Point).
	     */
	    this.map[i] = new Planet(PLANET_NAMES[i], locationPoint, player);
	}

	player.setCurrentPlanet(map[rand.nextInt(this.map.length)]);
	player.getCurrentPlanet().setDrawSize(DRAWSIZE);
    }
    
    /**
     * Value for the first space dimension
     */
    
    private static final int WIDTHDIM = 300;
    
    /**
     * Value for the second space dimension
     */
    
    private static final int HEIGHTDIM = 200;
    
    /**
     * Standard constructor, which takes in the Maximum possible number of
     * planets (which is equal to the number of Planet Names), and assigns it a
     * standard Dimension, which is set to 300x200 by default.
     * @param player The user
     */
    public Space(Player player) {
	this(PLANET_NAMES.length, new Dimension(WIDTHDIM, HEIGHTDIM), player);
    }

    /**
     * Used to get the array of Planets in Space.
     * 
     * @return An array of all the planets in Space.
     */
    public Planet[] getPlanets() {
	return map;
    }

    /**
     * Used to get the grid size dimensions on which the planets are placed.
     * 
     * @return The Dimensions of the grid on which the planets are placed.
     */
    public Dimension getGridSize() {
	return gridSize;
    }

    /**
     * Checks to see if the player can travel to the destination planet -
     * returning true if they have the fuel to get there, and false if they do
     * not.
     * 
     * @param destination
     *            The planet to which the player wishes to travel
     * @return True if the player has the fuel to get there, and false if he/she
     *         does not
     */
    public boolean canTravel(Planet destination) {
	final Planet currPlanet = player.getCurrentPlanet();
	final int distance = calcDistance(currPlanet, destination);
	if (Game.Player.getShip().getCurrFuelLevel() - distance >= 0) {
	    return true;
	}
	if (Game.Player.getShip().getCurrFuelLevel() - distance == Game.Player
		.getShip().getCurrFuelLevel()) {
	    System.out.println("You're already on this planet!");
	    return false;
	}
	System.out.println("Insufficient fuel to travel to planet "
		+ destination.getName() + ". You have "
		+ Game.Player.getShip().getCurrFuelLevel()
		+ " units of fuel left.");
	return false;
    }

    /**
     * Checks to see if the player is allowed to travel to the destination
     * planet. If the player has the fuel to get there, the fuel it takes to
     * arrive at the planet is subtracted from his/her total amount of fuel, and
     * the player's location is changed to the new planet.
     * 
     * @param destination
     *            The planet the player wishes to travel to
     */
    public void travel(Planet destination) {
	if (canTravel(destination)) {
	    String arrivalMessage = "";
	    final Planet currPlanet = player.getCurrentPlanet();
	    final int initialFuel = Game.Player.getShip().getCurrFuelLevel();
	    final int distance = calcDistance(currPlanet, destination);
	    Game.Player.getShip().useFuel(distance);
	    Game.Player.setCurrentPlanet(destination);
	    // System.out.println("Welcome to " + destination.getName() + "!");
	    // System.out.println("It took you " + distance +
	    // " units fuel to get here. You " +
	    // "started out with " + initialFuel + " units, you " +
	    // "have " + Game.Player.getShip().getCurrFuelLevel() +
	    // " units of fuel left.");
	    //
	    arrivalMessage = "Welcome to " + destination.getName() + "!\n"
		    + "It took you " + distance
		    + " units fuel to get here. You " + "started out with "
		    + initialFuel + " units, you " + "have "
		    + Game.Player.getShip().getCurrFuelLevel()
		    + " units of fuel left.";

	    if (distance != 0) {
		Game.randomEventGenerator();
		if (!Game.Event.equals("none")) {
		    // System.out.println("This planet is suffering from " +
		    // Game.Event);

		    arrivalMessage = arrivalMessage
			    + "\nThis planet is suffering from " + Game.Event
			    + ".";

		}
		Game.randomCrisisGenerator();
		if (!Game.Crisis.equals("none")) {
		    // System.out.println("This planet is suffering from " +
		    // Game.Event);

		    arrivalMessage = arrivalMessage + "\n" + Game.Crisis;

		}
	    }

	    // Make a PopUp box with arrival information:
	    JOptionPane.showMessageDialog(Game.InitConfigScreen.getFrame(),
		    arrivalMessage);

	}

    }
    
    /**
     * Used for raising things to the second power.
     */
    
    private static final int SQUAREPOWER = 2;
    
    /**
     * Used for taking the square root.
     */
    
    private static final double SQUAREROOTPOWER = .5;
    
    /**
     * Calculates the Euclidean distance between Planet a and Planet b
     * 
     * @param a
     *            The first planet
     * @param b
     *            The second planet
     * @return The Euclidean distances between the two planets
     */
    public int calcDistance(Planet a, Planet b) {
	final int distance = (int) Math.pow((Math.pow( // $codepro.audit.disable lossOfPrecisionInCast
		a.getLocation().x - b.getLocation().x, SQUAREPOWER) + Math.pow(
		a.getLocation().y - b.getLocation().y, SQUAREPOWER)), (SQUAREROOTPOWER));
	return (int) (Math.ceil((double) distance / FUEL_DISTANCE_RATIO)); // $codepro.audit.disable lossOfPrecisionInCast
    }

    /**
     * Used to get the ratio between the distance of planets and fuel
     * expenditures.
     * 
     * @return The ratio between the fuel and the distance.
     */
    public int getFuelDistanceRatio() {
	return FUEL_DISTANCE_RATIO;
    }
    
    /**
     * @return The results of the default toString() method for a class
     */
    public String toString() {
	return this.toString();
    }
}
